import 'package:math_keyboard/src/foundation/node.dart';
import 'package:math_keyboard/src/widgets/math_field.dart';

/// Handler for special mathematical symbols and structures.
class SpecialSymbolsHandler {
  /// Adds an underset structure (text below a symbol).
  static void addUnderset(MathFieldEditingController controller) {
    final underset = createUnderset(controller.currentNode);
    
    // Add to current node
    controller.currentNode.removeCursor();
    controller.currentNode.addTeX(underset);
    
    // Move cursor to the first argument (the text below)
    controller.currentNode = underset.argNodes.first;
    controller.currentNode.courserPosition = 0;
    controller.currentNode.setCursor();
  }

  /// Adds an overset structure (text above a symbol).
  static void addOverset(MathFieldEditingController controller) {
    final overset = createOverset(controller.currentNode);
    
    // Add to current node
    controller.currentNode.removeCursor();
    controller.currentNode.addTeX(overset);
    
    // Move cursor to the first argument (the text above)
    controller.currentNode = overset.argNodes.first;
    controller.currentNode.courserPosition = 0;
    controller.currentNode.setCursor();
  }

  /// Adds a gradient (nabla) symbol.
  static void addGradient(MathFieldEditingController controller) {
    controller.addLeaf(r'\nabla');
  }

  /// Adds a partial derivative symbol.
  static void addPartial(MathFieldEditingController controller) {
    controller.addLeaf(r'\partial');
  }

  /// Adds an infinity symbol.
  static void addInfinity(MathFieldEditingController controller) {
    controller.addLeaf(r'\infty');
  }

  /// Adds a therefore symbol.
  static void addTherefore(MathFieldEditingController controller) {
    controller.addLeaf(r'\therefore');
  }

  /// Adds a because symbol.
  static void addBecause(MathFieldEditingController controller) {
    controller.addLeaf(r'\because');
  }

  /// Adds an approximately equal symbol.
  static void addApprox(MathFieldEditingController controller) {
    controller.addLeaf(r'\approx');
  }

  /// Adds a proportional to symbol.
  static void addPropto(MathFieldEditingController controller) {
    controller.addLeaf(r'\propto');
  }

  /// Adds a not equal symbol.
  static void addNotEqual(MathFieldEditingController controller) {
    controller.addLeaf(r'\neq');
  }

  /// Adds a less than or equal symbol.
  static void addLeq(MathFieldEditingController controller) {
    controller.addLeaf(r'\leq');
  }

  /// Adds a greater than or equal symbol.
  static void addGeq(MathFieldEditingController controller) {
    controller.addLeaf(r'\geq');
  }

  /// Adds a much less than symbol.
  static void addLl(MathFieldEditingController controller) {
    controller.addLeaf(r'\ll');
  }

  /// Adds a much greater than symbol.
  static void addGg(MathFieldEditingController controller) {
    controller.addLeaf(r'\gg');
  }

  /// Adds a plus-minus symbol.
  static void addPlusMinus(MathFieldEditingController controller) {
    controller.addLeaf(r'\pm');
  }

  /// Adds a minus-plus symbol.
  static void addMinusPlus(MathFieldEditingController controller) {
    controller.addLeaf(r'\mp');
  }

  /// Creates an underset structure.
  static TeXFunction createUnderset(TeXNode parent) {
    return TeXFunction(
      r'\underset',
      parent,
      [TeXArg.braces, TeXArg.braces],
    );
  }

  /// Creates an overset structure.
  static TeXFunction createOverset(TeXNode parent) {
    return TeXFunction(
      r'\overset',
      parent,
      [TeXArg.braces, TeXArg.braces],
    );
  }

  /// Creates a stackrel structure (similar to overset but with different spacing).
  static TeXFunction createStackrel(TeXNode parent) {
    return TeXFunction(
      r'\stackrel',
      parent,
      [TeXArg.braces, TeXArg.braces],
    );
  }

  /// Creates an overbrace structure.
  static TeXFunction createOverbrace(TeXNode parent) {
    return TeXFunction(
      r'\overbrace',
      parent,
      [TeXArg.braces],
    );
  }

  /// Creates an underbrace structure.
  static TeXFunction createUnderbrace(TeXNode parent) {
    return TeXFunction(
      r'\underbrace',
      parent,
      [TeXArg.braces],
    );
  }

  /// Creates a text structure for inserting text in math mode.
  static TeXFunction createText(TeXNode parent) {
    return TeXFunction(
      r'\text',
      parent,
      [TeXArg.braces],
    );
  }

  /// Creates a bold text structure.
  static TeXFunction createBold(TeXNode parent) {
    return TeXFunction(
      r'\mathbf',
      parent,
      [TeXArg.braces],
    );
  }

  /// Creates an italic text structure.
  static TeXFunction createItalic(TeXNode parent) {
    return TeXFunction(
      r'\mathit',
      parent,
      [TeXArg.braces],
    );
  }

  /// Creates a calligraphic text structure.
  static TeXFunction createCalligraphic(TeXNode parent) {
    return TeXFunction(
      r'\mathcal',
      parent,
      [TeXArg.braces],
    );
  }

  /// Creates a blackboard bold structure.
  static TeXFunction createBlackboard(TeXNode parent) {
    return TeXFunction(
      r'\mathbb',
      parent,
      [TeXArg.braces],
    );
  }

  /// Creates a fraktur text structure.
  static TeXFunction createFraktur(TeXNode parent) {
    return TeXFunction(
      r'\mathfrak',
      parent,
      [TeXArg.braces],
    );
  }

  /// Creates a sans-serif text structure.
  static TeXFunction createSansSerif(TeXNode parent) {
    return TeXFunction(
      r'\mathsf',
      parent,
      [TeXArg.braces],
    );
  }

  /// Creates a typewriter text structure.
  static TeXFunction createTypewriter(TeXNode parent) {
    return TeXFunction(
      r'\mathtt',
      parent,
      [TeXArg.braces],
    );
  }

  /// Map of common special symbols.
  static const Map<String, String> commonSymbols = {
    'alpha': r'\alpha',
    'beta': r'\beta',
    'gamma': r'\gamma',
    'delta': r'\delta',
    'epsilon': r'\epsilon',
    'zeta': r'\zeta',
    'eta': r'\eta',
    'theta': r'\theta',
    'iota': r'\iota',
    'kappa': r'\kappa',
    'lambda': r'\lambda',
    'mu': r'\mu',
    'nu': r'\nu',
    'xi': r'\xi',
    'omicron': r'\omicron',
    'pi': r'\pi',
    'rho': r'\rho',
    'sigma': r'\sigma',
    'tau': r'\tau',
    'upsilon': r'\upsilon',
    'phi': r'\phi',
    'chi': r'\chi',
    'psi': r'\psi',
    'omega': r'\omega',
    'Gamma': r'\Gamma',
    'Delta': r'\Delta',
    'Theta': r'\Theta',
    'Lambda': r'\Lambda',
    'Xi': r'\Xi',
    'Pi': r'\Pi',
    'Sigma': r'\Sigma',
    'Upsilon': r'\Upsilon',
    'Phi': r'\Phi',
    'Psi': r'\Psi',
    'Omega': r'\Omega',
  };

  /// Map of relation symbols.
  static const Map<String, String> relationSymbols = {
    'eq': '=',
    'neq': r'\neq',
    'approx': r'\approx',
    'equiv': r'\equiv',
    'sim': r'\sim',
    'simeq': r'\simeq',
    'cong': r'\cong',
    'propto': r'\propto',
    'lt': '<',
    'gt': '>',
    'leq': r'\leq',
    'geq': r'\geq',
    'll': r'\ll',
    'gg': r'\gg',
    'subset': r'\subset',
    'supset': r'\supset',
    'subseteq': r'\subseteq',
    'supseteq': r'\supseteq',
    'in': r'\in',
    'notin': r'\notin',
    'ni': r'\ni',
    'cup': r'\cup',
    'cap': r'\cap',
    'setminus': r'\setminus',
    'emptyset': r'\emptyset',
  };

  /// Map of binary operation symbols.
  static const Map<String, String> binaryOperationSymbols = {
    'plus': '+',
    'minus': '-',
    'times': r'\times',
    'div': r'\div',
    'cdot': r'\cdot',
    'ast': r'\ast',
    'star': r'\star',
    'circ': r'\circ',
    'bullet': r'\bullet',
    'pm': r'\pm',
    'mp': r'\mp',
    'oplus': r'\oplus',
    'ominus': r'\ominus',
    'otimes': r'\otimes',
    'oslash': r'\oslash',
    'odot': r'\odot',
    'wedge': r'\wedge',
    'vee': r'\vee',
  };

  /// Map of arrow symbols.
  static const Map<String, String> arrowSymbols = {
    'leftarrow': r'\leftarrow',
    'rightarrow': r'\rightarrow',
    'leftrightarrow': r'\leftrightarrow',
    'Leftarrow': r'\Leftarrow',
    'Rightarrow': r'\Rightarrow',
    'Leftrightarrow': r'\Leftrightarrow',
    'uparrow': r'\uparrow',
    'downarrow': r'\downarrow',
    'updownarrow': r'\updownarrow',
    'Uparrow': r'\Uparrow',
    'Downarrow': r'\Downarrow',
    'Updownarrow': r'\Updownarrow',
    'mapsto': r'\mapsto',
    'longmapsto': r'\longmapsto',
    'hookleftarrow': r'\hookleftarrow',
    'hookrightarrow': r'\hookrightarrow',
  };

  /// Map of miscellaneous symbols.
  static const Map<String, String> miscSymbols = {
    'infty': r'\infty',
    'partial': r'\partial',
    'nabla': r'\nabla',
    'angle': r'\angle',
    'triangle': r'\triangle',
    'square': r'\square',
    'diamond': r'\diamond',
    'clubsuit': r'\clubsuit',
    'diamondsuit': r'\diamondsuit',
    'heartsuit': r'\heartsuit',
    'spadesuit': r'\spadesuit',
    'therefore': r'\therefore',
    'because': r'\because',
    'exists': r'\exists',
    'nexists': r'\nexists',
    'forall': r'\forall',
    'top': r'\top',
    'bot': r'\bot',
    'vdash': r'\vdash',
    'dashv': r'\dashv',
    'models': r'\models',
  };

  /// Gets all available symbols organized by category.
  static Map<String, Map<String, String>> get allSymbols => {
    'Greek Letters': commonSymbols,
    'Relations': relationSymbols,
    'Binary Operations': binaryOperationSymbols,
    'Arrows': arrowSymbols,
    'Miscellaneous': miscSymbols,
  };

  /// Adds a symbol by name.
  static void addSymbolByName(MathFieldEditingController controller, String symbolName) {
    // Search through all symbol categories
    for (final category in allSymbols.values) {
      if (category.containsKey(symbolName)) {
        controller.addLeaf(category[symbolName]!);
        return;
      }
    }
    
    // If not found, add as-is (might be a custom symbol)
    controller.addLeaf(symbolName);
  }

  /// Checks if a symbol name exists in the predefined symbols.
  static bool isKnownSymbol(String symbolName) {
    for (final category in allSymbols.values) {
      if (category.containsKey(symbolName)) {
        return true;
      }
    }
    return false;
  }

  /// Gets the LaTeX representation of a symbol by name.
  static String? getSymbolLatex(String symbolName) {
    for (final category in allSymbols.values) {
      if (category.containsKey(symbolName)) {
        return category[symbolName];
      }
    }
    return null;
  }
}